<?php

declare(strict_types=1);

namespace Zms\Validate\Rule;

use Zms\Exception\ValidateException;
use Zms\Unit\Is;

/**
 * 域名检测
 */
class DomainValidate extends RuleAbstract
{
    /**
     * 默认错误消息
     * @var string
     */
    protected $_error = '{label}不是有效的域名';

    /**
     * 去除结尾符号
     * @var array
     */
    protected $_clear_ending = true;

    /**
     * 是否验证协议
     * @var array|bool
     */
    protected $_scheme = ['http', 'https'];


    /**
     * 是否允许携带端口
     * @var bool
     */
    protected $_port = true;


    /**
     * 是否允许IP
     * @var bool
     */
    protected $_ip = true;


    /**
     * 是否去掉结尾斜杠符号
     *
     * @param bool $clearEnding 需要检测的词汇数组
     * @return $this 当前实例
     */
    public function setClearEnding(bool $clearEnding): DomainValidate
    {
        $this->_clear_ending = $clearEnding;
        return $this;
    }


    /**
     * 是否验证协议
     * @param array|false $scheme 允许的协议
     * @return $this 当前实例
     * @throws ValidateException
     */
    public function setScheme($scheme): DomainValidate
    {
        if (is_array($scheme) || $scheme === false) {
            $this->_scheme = $scheme;
            return $this;
        }
        throw new ValidateException("domain attr scheme error");
    }


    /**
     * 是否允许域名携带端口
     * @param bool $port
     * @return $this 当前实例
     */
    public function setPort(bool $port): DomainValidate
    {
        $this->_port = $port;
        return $this;
    }


    /**
     * 是否允许IP地址
     * @param bool $ip
     * @return $this 当前实例
     */
    public function setIp(bool $ip): DomainValidate
    {
        $this->_ip = $ip;
        return $this;
    }


    /**
     * 开始验证
     *
     * @param mixed $value 待验证的值
     * @param string $label 字段标签，用于替换错误信息中的占位符
     * @return bool 验证是否通过
     */
    public function validate($value, string $label = ''): bool
    {
        if (is_string($value)) {
            // 如果值为空且 `_noSkipping` 为 `true`，直接返回 `true`
            if (Is::empty($value) && $this->_noSkipping === true) {
                $this->_value = $value;
                return true;
            }

            if ($this->_clear_ending) $value = rtrim($value, '/');
            $info = parse_url($value);
            //协议验证
            $scheme = $info['scheme'] ?? '';
            if (is_array($this->_scheme) && count($this->_scheme) > 0 && in_array($scheme, $this->_scheme, true) === false) {
                $this->addError($this->_error, $label);
                return false;
            } elseif ($this->_scheme === false && $scheme) {
                $this->addError($this->_error, $label);
                return false;
            }

            //端口验证
            $port = $info['port'] ?? '';
            if ($port) {
                if ($this->_port) {
                    if (!(is_int($port) && $port > 0 && $port <= 65535)) {
                        $this->addError($this->_error, $label);
                        return false;
                    }
                } else {
                    $this->addError($this->_error, $label);
                    return false;
                }
            }
            //域名验证
            $host = $info['host'] ?? $info['path'];
            if (preg_match('/^([a-z0-9]([-a-z0-9]*[a-z0-9])?\.)+[a-z]{2,}$/i', $host)) {
                $this->_value = $value;
                return true;
            } elseif ($this->_ip) {
                if (IpValidate::check($host) === true) {
                    $this->_value = $value;
                    return true;
                }
            }
        }
        $this->addError($this->_error, $label);
        return false;
    }
}